﻿using System;
using System.Runtime.InteropServices;
using System.Web.UI.WebControls;
using Microsoft.VisualBasic;
using FirstDayOfWeek = System.Web.UI.WebControls.FirstDayOfWeek;
using System.Globalization;
using System.Collections.Generic;
using System.Linq;

namespace System
{
    public static class DateTimeExtensions
    {
        public static string ToRelativeDateString(this DateTime date)
        {
            return GetRelativeDateValue(date, DateTime.Now);
        }

        public static string ToRelativeDateStringUtc(this DateTime date)
        {
            return GetRelativeDateValue(date, DateTime.UtcNow);
        }

        //public static int GetWeekOfMonth(this DateTime date)
        //{
        //    return 0 | date.Day / 7;
        //}

        public static DateTime GetPreviousBusinessDay(this DateTime date)
        {
            var previousDate = date.Date.AddDays(-1);

            if (previousDate.DayOfWeek == DayOfWeek.Saturday) previousDate = previousDate.AddDays(-1);
            if (previousDate.DayOfWeek == DayOfWeek.Sunday) previousDate = previousDate.AddDays(-2);

            return previousDate;
        }

        public static int GetWeekOfMonth(this DateTime date)
        {
            DateTime beginningOfMonth = new DateTime(date.Year, date.Month, 1);

            while (date.Date.AddDays(1).DayOfWeek != CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek)
                date = date.AddDays(1);

            return (int)Math.Truncate((double)date.Subtract(beginningOfMonth).TotalDays / 7f) + 1;
        } 

        private static string GetRelativeDateValue(DateTime date, DateTime comparedTo)
        {
            var diff = comparedTo.Subtract(date);

            if (diff.Days >= 7)
                return date.ToString("dd/MM/yyyy");
            if (diff.Days > 1)
                return string.Concat(diff.Days, " dias atrás");
            if (diff.Days == 1)
                return "ontem";
            if (diff.Hours >= 2)
                return string.Concat(diff.Hours, " horas atrás");
            if (diff.Minutes >= 60)
                return "mais de uma hora atrás";
            if (diff.Minutes >= 5)
                return string.Concat(diff.Minutes, " minutos atrás");

            return diff.Minutes >= 1 ? "poucos minutos atrás" : "menos de um minuto atrás";
        }

        public static bool IsBusinessDay(this DateTime date)
        {
            return
                date.DayOfWeek != DayOfWeek.Saturday &&
                date.DayOfWeek != DayOfWeek.Sunday;
        }
        public static int BusinessDaysTo(this DateTime fromDate, DateTime toDate, int maxAllowed = 0)
        {
            var ret = 0;
            var dt = fromDate;
            while (dt < toDate)
            {
                if (dt.IsBusinessDay()) ret++;
                if (maxAllowed > 0 && ret == maxAllowed) return ret;
                dt = dt.AddDays(1);
            }
            return ret;
        }

        public enum FirstWeekOfYear
        {
            System,
            Jan1,
            FirstFourDays,
            FirstFullWeek,
        }

        public enum DateInterval
        {
            Year,
            Quarter,
            Month,
            DayOfYear,
            Day,
            WeekOfYear,
            Weekday,
            Hour,
            Minute,
            Second,
        }

        public static long DateDifference(this DateTime date1, DateInterval interval, DateTime date2, DayOfWeek dayOfWeek = DayOfWeek.Sunday, FirstWeekOfYear weekOfYear = FirstWeekOfYear.Jan1)
        {
            var dayOfWeekVb = (Microsoft.VisualBasic.FirstDayOfWeek)dayOfWeek;
            var weekOfYearVb = (Microsoft.VisualBasic.FirstWeekOfYear)weekOfYear;
            var intervalVb = (Microsoft.VisualBasic.DateInterval)interval;

            return DateAndTime.DateDiff(intervalVb, date1, date2, dayOfWeekVb, weekOfYearVb);
        }

        public static int WeekOfYear(this DateTime date)
        {
            var day = (int)CultureInfo.CurrentCulture.Calendar.GetDayOfWeek(date);
            return CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(date.AddDays(4 - (day == 0 ? 7 : day)), CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
        }

        public static int BusinessDaysUntil(this DateTime firstDay, DateTime lastDay, params DateTime[] bankHolidays)
        {
            firstDay = firstDay.Date;
            lastDay = lastDay.Date;
            if (firstDay > lastDay)
                throw new ArgumentException("Incorrect last day " + lastDay);

            TimeSpan span = lastDay - firstDay;
            int businessDays = span.Days + 1;
            int fullWeekCount = businessDays / 7;
            // find out if there are weekends during the time exceedng the full weeks
            if (businessDays > fullWeekCount * 7)
            {
                // we are here to find out if there is a 1-day or 2-days weekend
                // in the time interval remaining after subtracting the complete weeks
                int firstDayOfWeek = (int)firstDay.DayOfWeek;
                int lastDayOfWeek = (int)lastDay.DayOfWeek;
                if (lastDayOfWeek < firstDayOfWeek)
                    lastDayOfWeek += 7;
                if (firstDayOfWeek <= 6)
                {
                    if (lastDayOfWeek >= 7)// Both Saturday and Sunday are in the remaining time interval
                        businessDays -= 2;
                    else if (lastDayOfWeek >= 6)// Only Saturday is in the remaining time interval
                        businessDays -= 1;
                }
                else if (firstDayOfWeek <= 7 && lastDayOfWeek >= 7)// Only Sunday is in the remaining time interval
                    businessDays -= 1;
            }

            // subtract the weekends during the full weeks in the interval
            businessDays -= fullWeekCount + fullWeekCount;

            // subtract the number of bank holidays during the time interval
            foreach (DateTime bankHoliday in bankHolidays)
            {
                DateTime bh = bankHoliday.Date;
                if (firstDay <= bh && bh <= lastDay)
                    --businessDays;
            }

            return businessDays;
        }
        public static bool IsLastFriday(this DateTime date)
        {
            var firstDayOfNextMonth = new DateTime(date.Year, date.Month, 1).AddMonths(1);
            int vector = (((int)firstDayOfNextMonth.DayOfWeek + 1) % 7) + 1;

            return firstDayOfNextMonth.AddDays(-vector).Date == date.Date;
        }

        //public static DateTime FromExcelSerialDate(this DateTime date, int serialDate)
        //{
        //    if (serialDate > 59) serialDate -= 1;   
        //    return new DateTime(1899, 12, 31).AddDays(serialDate);
        //}

        public static string ToMonthName(this DateTime dateTime)
        {
            return CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(dateTime.Month);
        }

        public static string ToShortMonthName(this DateTime dateTime)
        {
            return CultureInfo.CurrentCulture.DateTimeFormat.GetAbbreviatedMonthName(dateTime.Month);
        }
    }
}






